package com.nageoffer.shortlink.project.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nageoffer.shortlink.project.common.convention.exception.ServiceException;
import com.nageoffer.shortlink.project.dao.entity.ShortLinkDO;
import com.nageoffer.shortlink.project.dao.mapper.ShortLinkMapper;
import com.nageoffer.shortlink.project.dto.req.ShortLinkCreateReqDTO;
import com.nageoffer.shortlink.project.dto.req.ShortLinkPageReqDTO;
import com.nageoffer.shortlink.project.dto.resp.ShortLinkCreateRespDTO;
import com.nageoffer.shortlink.project.dto.resp.ShortLinkPageRespDTO;
import com.nageoffer.shortlink.project.service.ShortLinkService;
import com.nageoffer.shortlink.project.utils.HashUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.ThreadLocalRandom;

@Service
@Slf4j
@AllArgsConstructor
public class ShortLinkServiceImpl extends ServiceImpl<ShortLinkMapper, ShortLinkDO>
        implements ShortLinkService {

    private static final int MAX_RETRY_TIMES = 3;
    private static final int MAX_GENERATE_TIMES = 10;

    private final RBloomFilter<String> shortUriCreateCachePenetrationBloomFilter;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ShortLinkCreateRespDTO createShortLink(ShortLinkCreateReqDTO requestDTO) {
        validateRequest(requestDTO);

        ShortLinkDO shortLinkDO = buildShortLinkDO(requestDTO);

        for (int attempt = 0; attempt < MAX_RETRY_TIMES; attempt++) {
            String shortUri = generateShortLinkSuffix(requestDTO);
            String fullShortUrl = buildFullShortUrl(requestDTO.getDomain(), shortUri);

            shortLinkDO.setShortUri(shortUri);
            shortLinkDO.setFullShortUrl(fullShortUrl);

            try {
                baseMapper.insert(shortLinkDO);
                shortUriCreateCachePenetrationBloomFilter.add(fullShortUrl);
                log.info("短链接创建成功: {}", fullShortUrl);
                return buildResponse(shortLinkDO);
            } catch (DuplicateKeyException e) {
                log.warn("短链接冲突，第{}次重试: {}", attempt + 1, fullShortUrl);
                if (attempt == MAX_RETRY_TIMES - 1) {
                    throw new ServiceException("短链接生成失败，请稍后重试");
                }
            }
        }

        throw new ServiceException("短链接生成失败");
    }

    private String generateShortLinkSuffix(ShortLinkCreateReqDTO requestDTO) {
        for (int i = 0; i < MAX_GENERATE_TIMES; i++) {
            String input = requestDTO.getOriginUrl() +
                    System.nanoTime() +
                    ThreadLocalRandom.current().nextInt(100000);
            String shortUri = HashUtil.hashToBase62(input);
            String fullUrl = buildFullShortUrl(requestDTO.getDomain(), shortUri);
            if (!shortUriCreateCachePenetrationBloomFilter.contains(fullUrl)) {
                return shortUri;
            }
            // 数据库确认（防止布隆过滤器误判）
            if (!checkExistsInDatabase(fullUrl)) {
                return shortUri;
            }
        }
        throw new ServiceException("短链接生成失败，请稍后重试");
    }

    private String buildFullShortUrl(String domain, String shortUri) {
        return domain + "/" + shortUri;
    }

    /**
     * 检查短链接是否真实存在于数据库
     */
    private boolean checkExistsInDatabase(String fullShortUrl) {
        return baseMapper.selectCount(
                Wrappers.lambdaQuery(ShortLinkDO.class)
                        .eq(ShortLinkDO::getFullShortUrl, fullShortUrl)
                        .last("LIMIT 1")
        ) > 0;
    }

    private void validateRequest(ShortLinkCreateReqDTO requestDTO) {
        if (StrUtil.isBlank(requestDTO.getDomain()) ||
                StrUtil.isBlank(requestDTO.getOriginUrl())) {
            throw new ServiceException("域名或原始链接不能为空");
        }
        if (StrUtil.isBlank(requestDTO.getGid())) {
            throw new ServiceException("分组ID不能为空");
        }
    }

    private ShortLinkDO buildShortLinkDO(ShortLinkCreateReqDTO requestDTO) {
        return ShortLinkDO.builder()
                .domain(requestDTO.getDomain())
                .originUrl(requestDTO.getOriginUrl())
                .clickNum(0)
                .gid(requestDTO.getGid())
                .enableStatus(0)
                .createdType(requestDTO.getCreatedType())
                .validDateType(requestDTO.getValidDateType())
                .validDate(requestDTO.getValidDate())
                .describe(requestDTO.getDescribe())
                .build();
    }

    private ShortLinkCreateRespDTO buildResponse(ShortLinkDO shortLinkDO) {
        return ShortLinkCreateRespDTO.builder()
                .gid(shortLinkDO.getGid())
                .originUrl(shortLinkDO.getOriginUrl())
                .fullShortUrl(shortLinkDO.getFullShortUrl())
                .build();
    }


    @Override
    public IPage<ShortLinkPageRespDTO> pageShortLink(ShortLinkPageReqDTO requestParam) {
        // 构建查询条件
        LambdaQueryWrapper<ShortLinkDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
                .eq(ShortLinkDO::getGid, requestParam.getGid())
                .eq(ShortLinkDO::getDelFlag, 0)
                .orderByDesc(ShortLinkDO::getCreateTime);

        // 执行分页查询
        IPage<ShortLinkDO> shortLinkPage = baseMapper.selectPage(requestParam, queryWrapper);

        // 转换为响应对象
        return shortLinkPage.convert(item -> {
            ShortLinkPageRespDTO respDTO = new ShortLinkPageRespDTO();
            BeanUtils.copyProperties(item, respDTO);
            return respDTO;
        });
    }
}